﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml.Linq;

/// <summary>
/// The Common namespace.
/// </summary>
namespace WebChatApi.Common
{
    /// <summary>
    /// Abstract singleton class for framework.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class BaseFramework<T> : Singleton<T> where T : BaseFramework<T>, new()
    {
        /// <summary>
        /// The SQL connection key
        /// </summary>
        protected const string sqlConnectionKey = "SqlConnection";

        /// <summary>
        /// The is debug key
        /// </summary>
        protected const string isDebugKey = "IsDebug";

        /// <summary>
        /// The tracking traffic
        /// </summary>
        protected const string trackingTraffic = "TrackingTraffic";

        /// <summary>
        /// The timezone source
        /// </summary>
        protected const string timezoneSource = "TimezoneSource.xml";

        /// <summary>
        /// The session life cycle key
        /// </summary>
        protected const string sessionLifeCycleKey = "SessionLifeCycle";

        /// <summary>
        /// The reset password token life cycle key
        /// </summary>
        protected const string resetPasswordTokenLifeCycleKey = "ResetPasswordTokenLifeCycle";

        /// <summary>
        /// The timezones
        /// </summary>
        protected Dictionary<string, List<TimeZoneObject>> timezones = new Dictionary<string, List<TimeZoneObject>>();

        #region Properties

        /// <summary>
        /// Gets the base directory.
        /// </summary>
        /// <value>The base directory.</value>
        public static string BaseDirectory
        {
            get
            {
                return ConfigurationUtil.ApplicationBaseDirectory;
            }
        }

        /// <summary>
        /// Gets the SQL connection string.
        /// </summary>
        /// <value>The SQL connection string.</value>
        public static string SqlConnection
        {
            get
            {
                return ConfigurationUtil.Instance.GetValueByKey<string>(sqlConnectionKey);
            }
        }

        /// <summary>
        /// Gets the session life cycle.
        /// </summary>
        /// <value>The session life cycle.</value>
        public static Int32 SessionLifeCycle
        {
            get
            {
                return ConfigurationUtil.Instance.GetValueByKey<Int32>(sessionLifeCycleKey);
            }
        }

        /// <summary>
        /// Gets the reset password token life cycle.
        /// </summary>
        /// <value>The reset password token life cycle.</value>
        public static Int32 ResetPasswordTokenLifeCycle
        {
            get
            {
                return ConfigurationUtil.Instance.GetValueByKey<Int32>(resetPasswordTokenLifeCycleKey);
            }
        }
        /// <summary>
        /// Gets a value indicating whether this instance is debug.
        /// </summary>
        /// <value><c>true</c> if this instance is debug; otherwise, <c>false</c>.</value>
        public static bool IsDebug
        {
            get
            {
                return ConfigurationUtil.Instance.GetValueByKey<bool>(isDebugKey);
            }
        }

        /// <summary>
        /// Gets a value indicating whether [tracking traffic].
        /// </summary>
        /// <value><c>true</c> if [tracking traffic]; otherwise, <c>false</c>.</value>
        public static bool TrackingTraffic
        {
            get
            {
                return ConfigurationUtil.Instance.GetValueByKey<bool>(trackingTraffic);
            }
        }

        /// <summary>
        /// Gets the logger.
        /// </summary>
        /// <value>The logger.</value>
        public Logger Logger
        {
            get;
            protected set;
        }

        /// <summary>
        /// Gets the application identity.
        /// </summary>
        /// <value>The application identity.</value>
        public string ApplicationIdentity
        {
            get;
            protected set;
        }

        /// <summary>
        /// Gets the <see cref="System.String" /> with the specified setting key.
        /// </summary>
        /// <param name="settingKey">The setting key.</param>
        /// <returns>System.String.</returns>
        public string this[string settingKey]
        {
            get
            {
                return ConfigurationUtil.Instance.GetValueByKey<string>(settingKey);
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="BaseFramework{T}" /> class.
        /// </summary>
        public BaseFramework()
            : this(string.Empty)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BaseFramework{T}" /> class.
        /// </summary>
        /// <param name="applicationIdentity">The application identity.</param>
        protected BaseFramework(string applicationIdentity)
        {
            this.ApplicationIdentity = applicationIdentity;
            InitializeLogger();

            if (IsDebug)
            {
                Logger.LogMessageWithTimestamp("Logger and configuration is initialized. Settings count: " + ConfigurationUtil.Instance.SettingsCount);
            }
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Gets the setting by key.
        /// </summary>
        /// <typeparam name="OutputT">The type of the utput T.</typeparam>
        /// <param name="key">The key.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>``0.</returns>
        public OutputT GetSettingByKey<OutputT>(string key, OutputT defaultValue = default(OutputT))
        {
            OutputT result = ConfigurationUtil.Instance.GetValueByKey<OutputT>(key);
            return result == null ? defaultValue : result;
        }

        /// <summary>
        /// Gets the setting by key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>System.Object.</returns>
        public object GetSettingByKey(string key, object defaultValue = null)
        {
            object result = ConfigurationUtil.Instance.GetValueByKey<T>(key);
            return result == null ? defaultValue : result;
        }

        /// <summary>
        /// Gets the file path from app domain.
        /// </summary>
        /// <param name="relativePath">The relative path.</param>
        /// <returns>System.String.</returns>
        public string GetFilePathFromAppDomain(string relativePath)
        {
            return Path.Combine(BaseDirectory, relativePath);
        }

        /// <summary>
        /// Gets the time zone objects.
        /// </summary>
        /// <param name="cultureInfo">The culture info.</param>
        /// <returns>List{TimeZoneObject}.</returns>
        public List<TimeZoneObject> GetTimeZoneObjects(string cultureInfo)
        {
            return this.timezones.ContainsKey(cultureInfo) ? this.timezones[cultureInfo] : new List<TimeZoneObject>();
        }

        /// <summary>
        /// Gets the time zone object by key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="cultureInfo">The culture info.</param>
        /// <returns>TimeZoneObject.</returns>
        public TimeZoneObject GetTimeZoneObjectByKey(Guid key, string cultureInfo)
        {
            var collection = GetTimeZoneObjects(cultureInfo);
            return (from item in collection where item.Key == key select item).FirstOrDefault();
        }

        /// <summary>
        /// Gets the version.
        /// </summary>
        /// <returns>System.String.</returns>
        public string GetVersion()
        {
            return this.GetAssemblyVersion();
        }

        /// <summary>
        /// Gets the culture info by code.
        /// </summary>
        /// <param name="code">The code.</param>
        /// <returns>CultureInfo.</returns>
        public CultureInfo GetCultureInfoByCode(string code = null)
        {
            CultureInfo result = null;

            try
            {
                result = new CultureInfo(code);
            }
            catch { }

            if (result == null)
            {
                result = new CultureInfo("en-US");
            }

            return result;
        }

        #endregion

        #region Initialize methods

        /// <summary>
        /// Initializes the time zones.
        /// </summary>
        /// <exception cref="InitializationFailureException">TimeZones</exception>
        protected virtual void InitializeTimeZones()
        {
            try
            {
                XDocument xDocument = XDocument.Load(GetFilePathFromAppDomain(timezoneSource));

                foreach (var element in xDocument.Root.Elements("CultureInfo"))
                {
                    string cultureInfoCode = element.GetAttributeValue("Code");
                    if (!string.IsNullOrWhiteSpace(cultureInfoCode))
                    {
                        if (!this.timezones.ContainsKey(cultureInfoCode))
                        {
                            this.timezones.Add(cultureInfoCode, new List<TimeZoneObject>());
                        }
                        var container = this.timezones[cultureInfoCode];
                        foreach (var item in element.Elements())
                        {
                            container.Add(new TimeZoneObject
                            {
                                Key = item.GetAttributeValue("Key").ToGrid(Guid.NewGuid()).Value,
                                DisplayName = item.Value,
                                Offset = item.GetAttributeValue("Offset"),
                                StandardName = item.GetAttributeValue("StandardName")
                            });
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new InitializationFailureException("TimeZones", ex);
            }
        }

        /// <summary>
        /// Initializes the logger.
        /// </summary>
        /// <exception cref="InitializationFailureException">Logger</exception>
        protected virtual void InitializeLogger()
        {
            try
            {
                this.Logger = new Logger(this.ApplicationIdentity, true, false, this.ApplicationIdentity);
            }
            catch (Exception ex)
            {
                throw new InitializationFailureException("Logger", ex);
            }
        }


        #endregion

        /// <summary>
        /// Gets the request log.
        /// </summary>
        /// <param name="httpRequest">The HTTP request.</param>
        /// <returns>System.String.</returns>
        public static string GetRequestLog(HttpRequest httpRequest)
        {
            StringBuilder stringBuilder = new StringBuilder();

            if (httpRequest != null)
            {
                stringBuilder.AppendLine("Address: [" + httpRequest.Url.ToString() + "]");
                stringBuilder.AppendLine("Refer:" + (httpRequest.UrlReferrer == null ? "<null>" : httpRequest.UrlReferrer.ToString()));
                stringBuilder.AppendLine("User Address: [" + httpRequest.UserHostAddress + "]");

                byte[] buffer = new byte[httpRequest.InputStream.Length];
                httpRequest.InputStream.Read(buffer, 0, buffer.Length);
                httpRequest.InputStream.Position = 0;
                string soapMessage = Encoding.ASCII.GetString(buffer);

                stringBuilder.AppendLine("Data: ");
                stringBuilder.AppendLine(soapMessage.GetStringValue());
            }

            return stringBuilder.ToString();
        }

        /// <summary>
        /// Handles the exception.
        /// </summary>
        /// <param name="exception">The exception.</param>
        /// <param name="operationIdentity">The operation identity.</param>
        /// <param name="operatorIdentity">The operator identity.</param>
        /// <param name="data">The data.</param>
        /// <returns>BaseServiceException.</returns>
        public BaseServiceException HandleException(BaseException exception, string operationIdentity, string operatorIdentity = null, object data = null)
        {
            if (exception != null)
            {
                BaseException baseException = null;

                if (exception.ErrorCode == BaseException.FaultCode.UnauthorizedOperation)
                {
                    baseException = new UnauthorizedOperationException(operationIdentity.GetStringValue(), operatorIdentity.GetStringValue(), exception, data);
                }
                else
                {
                    baseException = new OperationFailureException("QueryDiseaseName", exception, data);
                }

                Logger.LogException(baseException);
                return baseException.ToServiceException();
            }
            else
            {
                return new UnknownServiceException();
            }
        }
    }
}
